/**
* TG_Coder: Dzlua
* Email: 505544956@qq.com
* Time: 2017/05/10
*/
#include "Parser.h"

#define PASER_USERNAME "Dzlua"

namespace parser {

TGIniData::TGIniData()
{
	m_group = "0";
}

TGIniData::TGIniData(std::string group, std::string m_key, std::string m_value)
{
	m_group = group;
}

void TGIniData::Parse(std::string m_text)
{
	std::string m_key = "";
	std::string m_value = "";
	std::vector<std::string> m_arr;
	Common::Str_Util::Splite_String2(m_text, "\r\n", m_arr);

	std::string m_line = "";
	int size = (int)m_arr.size();
	for (int i = 0; i < size; i++){
		m_line = Common::Str_Conv::trim(m_arr[i]);
		if (!m_line.empty()){
			if (m_line.find("[", 0) == 0 && m_line.find("]", 0) > 0){
				Common::Str_Util::Replace_All(m_line, "[", "");
				Common::Str_Util::Replace_All(m_line, "]", "");
				m_group = m_line;
			}
			else{
				size_t pos = m_line.find("=", 0);
				if (pos != std::string::npos){
					Common::Str_Util::Replace_All(m_line, "{", "");
					Common::Str_Util::Replace_All(m_line, "}", "");
					m_key = m_line.substr(0, pos);
					m_value = m_line.substr(pos + 1);
					if (!m_key.empty() && !m_value.empty()){
						Add(m_key, m_value);
					}
				}
			}
		}
	}
}

int	TGIniData::GetSize()
{
	return (int)m_data.size();
}

void TGIniData::Add(std::string m_key, std::string m_value)
{
	if (IsExists(m_key)){
		m_data.find(m_key)->second = m_value;
	}
	else{
		m_data.insert(make_pair(m_key, m_value));
		m_keyArr.push_back(m_key);
	}
}

bool TGIniData::IsExists(std::string m_key)
{
	return Common::Arr_Util::Map_IsExistsA(m_data, m_key);
}

void TGIniData::Del(std::string m_key)
{
	Common::Arr_Util::Map_DelA(m_data, m_key);
	Common::Arr_Util::Vector_Del(m_keyArr, m_key);
}

std::string TGIniData::GetValue(std::string m_key)
{
	std::string m_retu = "";
	Common::Arr_Util::Map_GetA(m_data, m_key, m_retu);
	return m_retu;
}

std::string  TGIniData::ToString()
{
	std::string m_retu;
	m_retu.append("[" + m_group + "]\r\n");
	int size = m_keyArr.size();
	for (int i = 0; i < size; i++){
		m_retu.append(m_keyArr[i] + "={" + GetValue(m_keyArr[i]) + "}\r\n");
	}
	//std::map<std::string, std::string>::iterator itr = m_data.begin();
	//while (itr != m_data.end()){
	//	m_retu.append(itr->first + "={" + itr->second + "}\r\n");
	//	itr++;
	//}
	m_retu.append("\r\n");
	return m_retu;
}

//-----------------------------------------------//

void TGIniParse::Parse(std::string m_text)
{
	std::vector<std::string> m_arr;
	Common::Str_Util::Splite_String2(m_text, "\r\n", m_arr);

	std::vector<std::string> m_groupArr;
	std::string m_groupStr = "";

	std::string m_line = "";
	int size = (int)m_arr.size();
	for (int i = 0; i < size; i++){
		m_line = Common::Str_Conv::trim(m_arr[i]);
		if (m_line.find("//") == 0 || m_line.find("/*") == 0)
			continue;
		if (!m_line.empty()){
			if (m_line.find("[", 0) == 0 && m_line.find("]", 0) > 0){
				if (!m_groupStr.empty()){
					m_groupArr.push_back(m_groupStr);
					m_groupStr = "";
				}
				m_groupStr.append(m_line + "\r\n");
			}
			else{
				m_groupStr.append(m_line + "\r\n");
			}
		}
	}
	if (!m_groupStr.empty()){
		m_groupArr.push_back(m_groupStr);
		m_groupStr = "";
	}

	size = (int)m_groupArr.size();
	for (int i = 0; i < size; i++){
		TGIniData tgIniData;
		tgIniData.Parse(m_groupArr[i]);
		m_data.push_back(tgIniData);
	}
}

std::string TGIniParse::ToString()
{
	std::string m_retu;
	int size = (int)m_data.size();
	for (int i = 0; i < size; i++){
		m_retu.append(m_data[i].ToString());
	}
	return m_retu;
}

bool TGIniParse::GetGroupData(std::string m_group, std::vector<TGIniData>& m_arr)
{
	bool m_retu = false;
	int size = (int)m_data.size();
	for (int i = 0; i < size; i++){
		if (m_data[i].m_group == m_group){
			m_arr.push_back(m_data[i]);
			m_retu = true;
		}
	}
	return m_retu;
}

bool TGIniParse::GetGroupData2(std::string m_group, std::vector<TGIniData>& m_arr)
{
	bool m_retu = false;
	int size = (int)m_data.size();
	for (int i = 0; i < size; i++){
		if ((int)m_data[i].m_group.find(m_group) == 0){
			m_arr.push_back(m_data[i]);
			m_retu = true;
		}
	}
	return m_retu;
}

std::vector<std::string> TGIniParse::GetValues(std::string m_group, std::string m_key, bool isfirst)
{
	std::vector<std::string> m_arr;
	int size = (int)m_data.size();
	for (int i = 0; i < size; i++){
		if (m_data[i].m_group == m_group && m_data[i].IsExists(m_key)){
			m_arr.push_back(m_data[i].GetValue(m_key));
			if (isfirst)
				break;
		}
	}
	return m_arr;
}

bool TGIniParse::GetValue(std::string m_group, std::string m_key, std::string& m_value)
{
	bool m_retu = false;
	std::vector<std::string> m_arr = GetValues(m_group, m_key, TRUE);
	if (m_arr.size() > 0){
		m_value = m_arr[0];
		m_retu = TRUE;
	}
	return m_retu;
}

//--------------------------------//
VData* ParserDataFile(std::string path)
{
	std::string contentstr;
	Common::ReadTxtFileA(path, contentstr);
	contentstr = Common::Str_Conv::GBK2UTF8(contentstr);
	if (contentstr.empty())
		return nullptr;

	TGIniParse iniParse;
	iniParse.Parse(contentstr);
	std::vector<TGIniData> arr;
	iniParse.GetGroupData("GAMEFORM", arr);
	int size = (int)arr.size();
	TGLoginData* loginData = NULL;
	std::string tmp;
	std::string tmpW;
    VData* vdata = new VData;

    for (int i = 0; i < size; i++){
        loginData = new TGLoginData;

		loginData->name = arr[i].GetValue("name");
		loginData->platform = arr[i].GetValue("platform");
		loginData->url = arr[i].GetValue("url");
		loginData->username = arr[i].GetValue("username");

		Common::Base64::Decode(arr[i].GetValue("pwd"), tmpW);
		loginData->pwd = tmpW;

		loginData->pagenote = arr[i].GetValue("pagenote");
		loginData->gamespeed = 1.0;
		loginData->isAutoLogin = false;
		loginData->loginnum = 1;

		tmp = arr[i].GetValue("gamespeed");
		if (!tmp.empty())
			loginData->gamespeed = Common::Str_Conv::StrToNum<float>(tmp);

		tmp = arr[i].GetValue("autologin");
		if (!tmp.empty())
			loginData->isAutoLogin = Common::Str_Conv::StrToNum<int>(tmp) == 1;

		tmp = arr[i].GetValue("loginnum");
		if (!tmp.empty())
			loginData->loginnum = Common::Str_Conv::StrToNum<int>(tmp);
        
        vdata->push_back(loginData);
	}

    return vdata;
}

void ReleaseVData(VData* vdata)
{
    if (!vdata) return;

    for (auto iter = vdata->begin(); iter != vdata->end(); ++iter) {
		if (!(*iter)) continue;
        delete (*iter);
    }
    delete vdata;
}

Json::Value _GetGameValue(Json::Value& dzlua, std::string& url)
{
	if (url.empty()) return Json::Value();

	int size = dzlua.size();
	for (int i = 0; i < size; ++i) {
		if (url == dzlua[i]["game"].asString())
			return dzlua[i];
	}
	return Json::Value();
}

void _SetGameValue(Json::Value& dzlua, Json::Value& game)
{
	std::string url = game["game"].asString();
	if (url.empty()) return;

	int size = dzlua.size();
	for (int i = 0; i < size; ++i) {
		if (url == dzlua[i]["game"].asString()) {
			dzlua[i] = game;
			return;
		}
	}
	dzlua.append(game);
}

Json::Value _GetUserValue(Json::Value& game, std::string& user, std::string& url)
{
	if (user.empty() || url.empty()) return Json::Value();

	Json::Value users = game["users"];
	int size = users.size();
	for (int i = 0; i < size; ++i) {
		if (user == users[i]["user"].asString()
				&& url == users[i]["url"].asString())
			return users[i];
	}
	return Json::Value();
}

void _SetUserValue(Json::Value& dzlua, std::string gamename, Json::Value& user)
{
	if (gamename.empty()) return;
	std::string username = user["user"].asString();
	std::string url = user["url"].asString();
	if (username.empty() || url.empty()) return;

	int size = dzlua.size();
	for (int i = 0; i < size; ++i) {
		if (gamename != dzlua[i]["game"].asString())
			continue;
		Json::Value users = dzlua[i]["users"];
		int sz = users.size();
		for (int j = 0; j < sz; ++j) {
			if (username == users[j]["user"].asString()
					&& url == users[j]["url"].asString() ) {
				dzlua[i]["users"][j] = user;
				return;
			}
		}
		dzlua[i]["users"].append(user);
		break;
	}
}

Json::Value _GetDzluaValue(const Json::Value& root, const std::string& username) {
	if (username.empty()) return Json::Value();
	return root[username];
}

bool _SetDzluaValue(Json::Value& root, const std::string& username, Json::Value& dzlua) {
	root[username] = dzlua;
	return true;
}

// old data file
bool _VData2Json(VData* vdata, Json::Value& root)
{
	if (!vdata) return false;

	Json::Value dzlua = _GetDzluaValue(root, PASER_USERNAME);

	for (auto iter = vdata->begin(); iter != vdata->end(); ++iter) {
		TGLoginData* data = (*iter);
		if (!data) continue;
		//url and name must not be empty.
		if (data->url.empty() || data->name.empty())
			continue;
		//add game info
		Json::Value game = _GetGameValue(dzlua, data->name);
		game["url"] = data->url; // DEFAULT_URL;
		game["game"] = data->name;
		game["login"] = data->isAutoLogin == 1;
		_SetGameValue(dzlua, game);
		//username and pwd must not be empty
		//add user info
		if (data->username.empty() || data->pwd.empty())
			continue;
		Json::Value user = _GetUserValue(game, data->username, data->url);
		user["user"] = data->username;
		user["pwd"] = data->pwd;
		user["note"] = data->pagenote;
		user["url"] = data->url;
		_SetUserValue(dzlua, data->name, user);
	}

	return _SetDzluaValue(root, PASER_USERNAME, dzlua);
}

// new data file
bool _AddJs2Root(const Json::Value& js, Json::Value& root) {
	Json::Value rdzlua = _GetDzluaValue(root, PASER_USERNAME);
	Json::Value jdzlua = _GetDzluaValue(js, PASER_USERNAME);
	if (!jdzlua.isArray()) return false;

	for (auto it = jdzlua.begin(); it != jdzlua.end(); ++it) {
		bool bappend = false;
		for (auto rit = rdzlua.begin(); rit != rdzlua.end(); ++rit) {
			if ( (*it)["game"].asString() == (*rit)["game"].asString() ) {
				Json::Value users = (*it)["users"];
				if (!users.isArray()) break;

				for (auto uit = users.begin(); uit != users.end(); ++uit) {
					Json::Value user = _GetUserValue(
							(*rit), 
							(*uit)["user"].asString(), 
							(*uit)["url"].asString() );
					user["user"] = (*uit)["user"];
					user["pwd"] = (*uit)["pwd"];
					user["note"] = (*uit)["note"];
					user["url"] = (*uit)["url"];
					_SetUserValue(rdzlua, (*rit)["game"].asString(), user);
				}

				bappend = true;
			}
		}
		// not find, add
		if (!bappend)
			rdzlua.append(*it);
	}

	return _SetDzluaValue(root, PASER_USERNAME, rdzlua);
}

Json::Value _ReadJsonValue(const std::string& path) {
	Json::Value ret;

	std::string enstr, jsstr;
	if (!Common::ReadTxtFileA(path, enstr))
		return ret;

	if (!Common::Base64::Decode(enstr, jsstr))
		return ret;

	Json::Reader reader;
	reader.parse(jsstr, ret);

	return ret;
}

bool _WriteJsToFile(const std::string& path, const Json::Value& js) {
	Json::FastWriter writer;
	std::string strjs, utf8str;
	
	utf8str = writer.write(js);

	//Encode
	if (Common::Base64::Encode(utf8str, strjs ) < 0)
		return false;

	//write to file
	if (!Common::WriteTxtFileA(path, strjs))
		return false;

	return true;
}

// old data file
bool WriteData2File(std::string path, VData* vdata, bool autoRelease)
{
	Json::Value js = _ReadJsonValue(path);

    if (!vdata) return false;

	if (!_VData2Json(vdata, js))
		goto ERR;

	if (!_WriteJsToFile(path, js))
		goto ERR;

	if (autoRelease)
		ReleaseVData(vdata);
	return true;
ERR:
	if (autoRelease)
		ReleaseVData(vdata);
	return false;
}

// new data file
bool WriteData2File(std::string path, const std::string& file) {
	Json::Value outjs = _ReadJsonValue(path);
	Json::Value injs = _ReadJsonValue(file);

	if (!_AddJs2Root(injs, outjs))
		return false;

	if (!_WriteJsToFile(path, outjs))
		return false;
	return true;
}

} // end namespace parser

#undef PASER_USERNAME
